/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package client.net;

import client.pipeConnector.IPipe;
import protocolHandler.ProtoMessageParser;
import protocolHandler.HeaderCodes;
import protocolHandler.ProtoMessageCreator;
import socketHandler.MessageSocketHandler;
import java.io.IOException;
import java.net.Socket;

/**
 *
 * @author Александр Емельянов (mr.lex91@gmail.com)
 */
public class Client {
    private IPipe pipeConnector;
    private ConnectParams paramConnection;
    private MessageSocketHandler socketHandler=null;
    private boolean connected=false;
    private boolean loggedIn=false;
    private Object syncLogeedIn=new Object();
    private Object syncConnected=new Object();

    public Client(IPipe pipeConnector, String host, int port)
    {
        this.pipeConnector=pipeConnector;
        paramConnection=new ConnectParams(host,port);
        ProtoMessageCreator creator=new ProtoMessageCreator();
        creator.setType("ConnectParams");
        creator.addHeader("Host", host);
        creator.addHeader("Port", String.valueOf(port));
        pipeConnector.Write(creator.Create());
    }

    private boolean connect()
    {
        try {
            socketHandler=new MessageSocketHandler(new Socket(paramConnection.getHost(), paramConnection.getPort()));
            perfomResponseTread();
	    pipeConnector.Write("InetInfo\n"+socketHandler.inetInfo());
            return true;
        } catch (IOException ex) {
            socketHandler=null;
            pipeConnector.Write("ErrConnectParams");
            return false;
        }
    }

    public void Start()
    {
        ProtoMessageParser parser=new ProtoMessageParser();
        while (true)
        {
            try{
                String msg=pipeConnector.Read();
                parser.Parse(msg);
                switch(HeaderCodes.getRequestHeaderCode(parser.getType()))
                {
                    case 1:
                    case 2:
                    case 3:
                    {
                        if (!isConnected())
                        {
                            if (!connect()) break;
                        }
                        setConnected(true);
                        socketHandler.Write(msg);
                        break;
                    }
                    case 4:
                    {
                        socketHandler.Write(msg);
                        setLoggedIn(false);
                        break;
                    }
                    case 5:
                    case 6:
                    case 7:
                    case 8:
                    case 10:
                    {
                        socketHandler.Write(msg);
                        break;
                    }
                    case 9:
                    {
                        if (socketHandler!=null) socketHandler.Close();
                        paramConnection=new ConnectParams(parser.getHeaderVal("Host"),Integer.valueOf(parser.getHeaderVal("Port")));
                        connect();
			break;
                    }
		    case 11:
		    {
			pipeConnector.Write(parser.getType()+"\n"+socketHandler.inetInfo());
		    }
                }
                parser.clear();
            }
            catch(IOException e)
            {
                socketHandler.Close();
                socketHandler=null;
            }
        }
    }

    private void perfomResponseTread()
    {
        new Thread(new Runnable() {

            @Override
            public void run() {
                ProtoMessageParser parser=new ProtoMessageParser();
                while(true)
                {
                    try {
                        String in=socketHandler.Read();
                        if (in.length()==0) {
                            socketHandler=null;
                            setConnected(false);
                            if (isLoggedIn())
                            {
                                setLoggedIn(false);
                                pipeConnector.Write("ConnectionClosed");
                            }
                            return;
                        }
                        parser.Parse(in);
                        switch(HeaderCodes.getResponseHeaderCode(parser.getType()))
                        {
                            case 14:
                            {
                                socketHandler.Close();
                                socketHandler=null;
                                setConnected(false);
                                setLoggedIn(false);
                                pipeConnector.Write("ConnectionClosed");
                                return;
                            }
                            case 4:
                            {
                                setLoggedIn(false);
                                pipeConnector.Write(in);
                                break;
                            }
                            case 12:
                            {
                                setLoggedIn(true);
                            }
                            default:pipeConnector.Write(in);
                        }

                        parser.clear();
                    } catch (IOException ex) {
                        socketHandler=null;
                        setConnected(false);
                        setLoggedIn(false);
                        pipeConnector.Write("ConnectionClosed");
                        return;
                    }
                }
            }
        }).start();
    }
    private void setLoggedIn(boolean loggedIn)
    {
        synchronized(syncLogeedIn)
        {
            this.loggedIn=loggedIn;
        }
    }
    private boolean isLoggedIn()
    {
        synchronized(syncLogeedIn)
        {
            return loggedIn;
        }
    }
    private void setConnected(boolean connected)
    {
        synchronized(syncConnected)
        {
            this.connected=connected;
        }
    }
    private boolean isConnected()
    {
        synchronized(syncConnected)
        {
            return connected;
        }
    }
}
